<script lang="ts" setup>
import { computed, onMounted, onBeforeUnmount, ref, nextTick, watch } from 'vue'
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
import { message } from 'ant-design-vue'
import { options } from './monaco.config'
import { useLocalStorage } from '@/utils/useLocalStorage'

const fontSize = useLocalStorage('editor_font_size', String(14))

const props = defineProps({
    diffEditor: { type: Boolean, default: false },
    width: { type: [String, Number], default: '700' },
    height: { type: [String, Number], default: '700' },
    theme: { type: String, default: 'vs-dark' },
    original: String,
    value: String,
    language: { type: String, default: 'text' },
})
const emit = defineEmits([
    'editorWillMount',
    'editorDidMount',
    'change',
    'update:languageOptions',
    'update:themeOptions',
    'keydown',
    'keyup',
    'keypress',
    'focus',
    'blur',
    'mousewheel',
    'cursorPositionChanged'
])
const style = computed(() => {
    const fixedWidth = props.width.toString().includes('%') ? props.width : `${props.width}px`
    const fixedHeight = props.height.toString().includes('%') ? props.height : `${props.height}px`
    return {
        width: fixedWidth,
        height: fixedHeight,
        'text-align': 'left'
    }
})
const breakpoints = new Set()
let breakpointDecorations = []
const editorContainer = ref<HTMLElement>()
let editor: any = null
onBeforeUnmount(() => {
    editor && editor.dispose()
})
import Mousetrap from 'mousetrap'
import { watchDebounced } from '@vueuse/core'
Mousetrap.bind('ctrl+s', (e) => {
    e.preventDefault()
    message.success('保存成功')
})
onBeforeUnmount(() => {
    Mousetrap.unbind('ctrl+s')
})
const themeOptions = [
    'vs-dark',
    'vs',
    'hc-black',
    'hc-light',
] as const
const initMonaco = () => {
    emit('editorWillMount', monaco)
    emit('update:languageOptions', ['c', 'cpp', 'python', 'javascript'])
    emit('update:themeOptions', themeOptions)

    const { value, language, diffEditor, original, theme } = props
    options.theme = theme ?? options.theme
    options.fontSize = parseInt(fontSize.value) || options.fontSize
    // editor.updateOptions(value)
    editor = monaco.editor[diffEditor ? 'createDiffEditor' : 'create'](
        editorContainer.value as HTMLElement,
        {
            value: value,
            language: language,
            ...options,
        }
    )
    diffEditor && _setModel(value, original)
    const _editor: any = _getEditor()
    _editor?.onDidChangeModelContent((event) => {
        const value = _editor.getValue()
        if (props.value !== value) {
            emit('change', value, event)
        }
    })
    _editor?.onMouseDown((event) => {
        if (event.target.type === monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN) {
            const lineNumber = event.target.position.lineNumber
            if (breakpoints.has(lineNumber)) breakpoints.delete(lineNumber)
            else breakpoints.add(lineNumber)
            updateBreakpoints()
        }
    })
    _editor?.onKeyDown((event) => {
        emit('keydown', event)
    })
    _editor?.onKeyUp((event) => {
        emit('keyup', event)
    })
    _editor?.onDidFocusEditorWidget(() => {
        emit('focus')
    })
    _editor?.onDidBlurEditorWidget(() => {
        emit('blur')
    })
    _editor?.onMouseWheel((event) => {
        emit('mousewheel', event)
    })
    _editor?.onDidChangeCursorPosition((event) => {
        emit('cursorPositionChanged', event)
    })
    emit('editorDidMount', editor)
}
const _setModel = (value, original) => {
    const { language } = props
    const originalModel = monaco.editor.createModel(original, language)
    const modifiedModel = monaco.editor.createModel(value, language)
    editor.setModel({
        original: originalModel,
        modified: modifiedModel
    })
}
const _setValue = (value) => {
    let editor = _getEditor()
    if (editor) return editor.setValue(value)
}
const _getEditor = () => {
    if (!editor) return null
    return props.diffEditor ? editor.modifiedEditor : editor
}
const _getValue = () => {
    let editor = _getEditor()
    if (!editor) return ''
    return editor.getValue()
}
const _setOriginal = () => {
    const { original } = editor.getModel()
    original.setValue(original)
}
const updateBreakpoints = () => {
    breakpointDecorations = _getEditor().deltaDecorations(
        breakpointDecorations,
        Array.from(breakpoints).map((lineNumber) => ({
            range: new monaco.Range(lineNumber as number, 1, lineNumber as number, 1),
            options: {
                isWholeLine: true,
                glyphMarginClassName: 'breakpoint'
            }
        }))
    )
    console.log(breakpointDecorations)
}

onMounted(() => nextTick(initMonaco))

watch(
    () => props.language,
    () => {
        if (!editor) return
        if (props.diffEditor) {
            const { original, modified } = editor.getModel()
            monaco.editor.setModelLanguage(original, props.language)
            monaco.editor.setModelLanguage(modified, props.language)
        } else monaco.editor.setModelLanguage(editor.getModel(), props.language)
    }
)
watch(
    () => props.original,
    () => {
        _setOriginal()
    }
)
watch(
    [ () => props.value, () => props.theme, fontSize],
    ([propValue, themeVal, fontSizeVal]) => {
        // 2. 处理 value：避免循环更新
        if (propValue !== _getValue()) {
            _setValue(propValue);
        }
        // 3. 处理 theme：防抖避免高频更新
        if (themeVal !== options.theme) {
            options.theme = themeVal;
            monaco.editor.setTheme(themeVal);
        }
        // 4. 更新字体大小
        editor?.updateOptions({ fontSize: parseInt(fontSizeVal) });
    },
    { deep: false } // 显式关闭深度监听
);
watchDebounced(
    fontSize,
    (value) => {
        editor?.updateOptions({ fontSize: parseInt(value) });
    },
    { debounce: 300 }
);
</script>
<template>
    <div ref="editorContainer" class="monaco-editor-vue3" :style="style as any" />
</template>

<style>
.breakpoint {
    z-index: 999 !important;
    background: url('./assets/breakpoint.svg') no-repeat center !important;
    background-size: 1.5625rem 1.5625rem !important;
    width: 1.5625rem !important;
    height: 1.5625rem !important;
}
</style>
